package main.algorithms.aco;

import java.util.ArrayList;

import main.problemstruktur.Knoten;
import main.problemstruktur.Problem;

public abstract class AcoMetaHeuristic extends Thread {

	protected ArrayList<Ant> ants;
	private boolean terminated;
	protected ACOProblem problem;
	private int bestTourSoFarGoodness = Integer.MAX_VALUE;
	private ArrayList<Knoten> bestTourSoFar;
	protected Double tau0;

	public AcoMetaHeuristic(Problem problem) {
		readInstance(problem);
	}

	public void run() {
		synchedRun();
	}

	protected synchronized void synchedRun() {
		initializeProblem(problem);
		int instanz=1;
		while (!terminated && !terminate()) {
			System.out.println("Instanz: "+instanz+++"Güte: "+getBestTourSoFarGoodness());
			constructAntSolutions();
			daemonAction();
			updateStatistics();
			updatePheromon();
		}
	}

	protected void initializeProblem(ACOProblem problem) {
		initializeAnts();
		initializeParameters();
		initializePheromon();
		initializeStatistics();
		// updateBestTourSoFar();
	}

	private void initializePheromon() throws Tau0NotImplementedException {
		if (tau0 == null)
			throw (new Tau0NotImplementedException());
		else {
			problem.initializePheromon(tau0);
		}
	}

	protected abstract void initializeStatistics();

	protected void constructAntSolutions() {
		initializeAnts();
		// TODO: Muss auf neuen Alg angepasst werden;
		for (int k = 0; k < ants.size(); k++)

			makeSolution(ants.get(k));
	}

	protected abstract void makeSolution(Ant ant);

	protected double computeLength(int k) {
		// TODO: Hier muss die Güte der Lösung gepostet werden, e.g. wie viele
		// Knoten nicht zugewiesen werden konnten.F
		Ant ant = ants.get(k);
		
		return 1.0/(ant.getMissingMemberCount()+1.0);
	}

	/**
	 * Die Arraylist muss erstellt und eine bestimmte Anzahl an Ants generiert
	 * werden.
	 */
	protected abstract void initializeAnts();

	/**
	 * Die Parameter werden als globale Variablen angelegt und müssen in dieser
	 * Methode inizialisiert werden.
	 */
	protected abstract void initializeParameters();

	/**
	 * Es muss ein Wahrscheinlichkeitsmodell hinterlegt werden, dass
	 * anschließend einen zusätzlichen Knoten die Methode
	 * {@link Ant#setTourInstanze(int, Integer)} aufruft.
	 * 
	 * @param ant
	 * @param step
	 */
	protected abstract void makeDecision(Ant ant, int step);

	/**
	 * Zusaetzliche Aktionen, die nach der Lösungsfindung ausgeführt werden
	 * sollen.
	 */
	protected abstract void daemonAction();

	/**
	 * Methode die die Pheromonwerte der Connections updatet.
	 */
	protected abstract void updatePheromon();

	protected void updateStatistics() {
		if (getBestSolutionsGoodness() < bestTourSoFarGoodness) {
			setBestTourSoFarGoodness(getBestSolutionsGoodness());
			setBestTourSoFar(getBestSolution());
			// updateBestTourSoFar();
		}
	}

	/**
	 * Terminierungsbedingung
	 * 
	 * @return true, wenn der Algorithmus terminiert werden soll
	 */
	protected abstract boolean terminate();

	/**
	 * @return Gibt die Kosten der besten Lösung der Iteration zurück
	 */
	public abstract int getBestSolutionsGoodness();

	/**
	 * @return Gibt die beste Lösung der Iteration zurück
	 */
	public abstract ArrayList<Knoten> getBestSolution();

	private void readInstance(Problem problem) {
		this.problem = new ACOProblem(problem);
	}

	public void setTerminated() {
		terminated = true;
	}

	public ArrayList<Knoten> getBestTourSoFar() {
		return bestTourSoFar;
	}

	private void setBestTourSoFar(ArrayList<Knoten> bestTourSoFar) {
		this.bestTourSoFar = bestTourSoFar;
	}

	public int getBestTourSoFarGoodness() {
		return bestTourSoFarGoodness;
	}

	private void setBestTourSoFarGoodness(int i) {
		this.bestTourSoFarGoodness = i;
	}

	// private void updateBestTourSoFar() {
	// problem.setBestTourSoFarValue(getBestTourSoFarValue());
	// problem.setBestTourSoFar(getBestTourSoFar());
	// }

}
